# uncompyle6 version 3.6.3
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.5 (default, Nov  7 2019, 10:50:52)
# [GCC 8.3.0]
# Embedded file name: ulang\CodeGen\ulgen.py
"""
ulgen
~~~~~
Extension to ast that allow ast -> ulang code generation.
"""

from ast import *

BINOP_SYMBOLS = {}
BINOP_SYMBOLS[Add] = "+"
BINOP_SYMBOLS[Sub] = "-"
BINOP_SYMBOLS[Mult] = "*"
BINOP_SYMBOLS[Div] = "/"
BINOP_SYMBOLS[Mod] = "%"
BINOP_SYMBOLS[Pow] = "^"
BINOP_SYMBOLS[LShift] = "<<"
BINOP_SYMBOLS[RShift] = ">>"
BINOP_SYMBOLS[BitOr] = "|"
BINOP_SYMBOLS[BitXor] = "@"
BINOP_SYMBOLS[BitAnd] = "&"
BINOP_SYMBOLS[FloorDiv] = "/"
BOOLOP_SYMBOLS = {}
BOOLOP_SYMBOLS[And] = "and"
BOOLOP_SYMBOLS[Or] = "or"
CMPOP_SYMBOLS = {}
CMPOP_SYMBOLS[Eq] = "=="
CMPOP_SYMBOLS[NotEq] = "!="
CMPOP_SYMBOLS[Lt] = "<"
CMPOP_SYMBOLS[LtE] = "<="
CMPOP_SYMBOLS[Gt] = ">"
CMPOP_SYMBOLS[GtE] = ">="
CMPOP_SYMBOLS[Is] = "==="
CMPOP_SYMBOLS[IsNot] = "!=="
CMPOP_SYMBOLS[In] = "in"
CMPOP_SYMBOLS[NotIn] = "not in"
UNARYOP_SYMBOLS = {}
UNARYOP_SYMBOLS[Invert] = "~"
UNARYOP_SYMBOLS[Not] = "!"
UNARYOP_SYMBOLS[UAdd] = "+"
UNARYOP_SYMBOLS[USub] = "-"


def to_source(node, indent_with="  "):
    """ 
    This function can covert a node tree back into ulang source code.
    This is useful for implementing a simple python to ulang tool.
    """
    header = "/* This file is auto-generated by the command `ulang -s `. */\n"
    generator = ULangGenerator(indent_with, header=header)
    generator.visit(node)
    return "".join(generator.result)


class ULangGenerator(NodeVisitor):
    """
    This visitor is able to transform a well formed syntax tree into
    the equivalent ulang source code.
    """
    __module__ = __name__
    __qualname__ = "ULangGenerator"

    def __init__(self, indent_with, add_line_info=False, header=None):
        self.result = []
        self.indent_with = indent_with
        self.add_line_info = add_line_info
        self.indentation = 0
        self.new_lines = 0
        self.classes = []
        if header is not None:
            self.result.append(header)

    def write(self, x):
        if not isinstance(x, str):
            raise AssertionError
        if self.new_lines:
            if self.result:
                self.result.append("\n" * self.new_lines)
            self.result.append(self.indent_with * self.indentation)
            self.new_lines = 0
        self.result.append(x)

    def newline(self, node=None, extra=0):
        self.new_lines = max(self.new_lines, 1 + extra)
        if node is not None:
            if self.add_line_info:
                self.write("// line: %s" % node.lineno)
                self.new_lines = 1

    def body(self, statements):
        self.write(" {")
        self.indentation += 1
        for stmt in statements:
            self.visit(stmt)

        self.indentation -= 1
        self.newline()
        self.write("}")

    def class_body(self, statements):
        self.write(" {")
        self.indentation += 1
        stmts = []
        for stmt in statements:
            if isinstance(stmt, FunctionDef) or isinstance(stmt, ClassDef):
                if stmts:
                    self.newline()
                    self.body(stmts)
                    stmts = []
                self.visit(stmt)
            else:
                stmts.append(stmt)

        if stmts:
            self.newline()
            self.body(stmts)
        self.indentation -= 1
        self.newline()
        self.write("}")

    def signature(self, node):
        want_comma = []

        def write_comma():
            if want_comma:
                self.write(", ")
            else:
                want_comma.append(True)

        padding = [None] * (len(node.args) - len(node.defaults))
        for arg, default in zip(node.args, padding + node.defaults):
            write_comma()
            self.visit(arg)
            if default is not None:
                self.write("=")
                self.visit(default)

        if node.vararg is not None:
            write_comma()
            self.write("*" + node.vararg)
        if node.kwarg is not None:
            write_comma()
            self.write("**" + node.kwarg)

    def visit_Assert(self, node):
        self.newline(node)
        self.write("assert (")
        self.visit(node.test)
        if node.msg is not None:
            self.write(", ")
            self.visit(node.msg)

    def visit_Assign(self, node):
        self.newline(node)
        for idx, target in enumerate(node.targets):
            if idx > 0:
                self.write(", ")
            self.visit(target)

        self.write(" = ")
        self.visit(node.value)

    def visit_AugAssign(self, node):
        self.newline(node)
        self.visit(node.target)
        self.write(" " + BINOP_SYMBOLS[type(node.op)] + "= ")
        self.visit(node.value)

    def visit_ImportFrom(self, node):
        self.newline(node)
        self.write("using ")
        for idx, item in enumerate(node.names):
            if idx:
                self.write(", ")
            self.write(item.name)

        if node.module:
            self.write(" in %s%s" % ("." * node.level, node.module))
        else:
            self.write(" in %s" % ("." * node.level))
        for item in node.names:
            self.visit(item)

    def visit_Import(self, node):
        self.newline(node)
        self.write("using ")
        for idx, item in enumerate(node.names):
            if idx:
                self.write(", ")
            self.write(item.name)

        for item in node.names:
            self.visit(item)

    def visit_Expr(self, node):
        self.newline(node)
        self.generic_visit(node)

    def visit_FunctionDef(self, node):
        self.newline(extra=1)
        self.newline(node)
        is_getter = False
        is_setter = False
        if node.decorator_list:
            for deco in decorator_list:
                if isinstance(deco, Name) and deco.id == "property":
                    is_getter = True
                else:
                    if isinstance(deco, Attribute) and deco.attr == "setter":
                        is_setter = True

        else:
            if is_getter:
                if is_setter:
                    raise AssertionError
                else:
                    if len(self.classes) > 0 and len(node.args.args) > 0:
                        if node.args.args[0].arg == "self":
                            node.args.args = node.args.args[1:]
                            if node.name == "__init__":
                                node.name = self.classes[(-1)]
                            node.name = "$" + node.name
                if is_getter or is_setter:
                    self.write("attr ")
            else:
                self.write("func ")
        self.write("%s%s(" % (node.name, " = " if is_setter else ""))
        self.visit(node.args)
        self.write(")")
        self.body(node.body)

    def visit_ClassDef(self, node):
        self.classes.append(node.name)
        self.newline(extra=2)
        self.newline(node)
        self.write("type %s" % node.name)
        for base in node.bases:
            if base != node.bases[0]:
                self.write(", ")
            else:
                self.write(" : ")
            self.visit(base)

        self.class_body(node.body)
        self.classes = self.classes[:-2]

    def visit_If(self, node):
        self.newline(node)
        self.write("if ")
        self.visit(node.test)
        self.body(node.body)
        while True:
            orelse = node.orelse
            if len(orelse) == 0:
                break
            elif len(orelse) == 1 and isinstance(orelse[0], If):
                node = orelse[0]
                self.write(" elif ")
                self.visit(node.test)
                self.body(node.body)
            else:
                self.write(" else")
                self.body(orelse)
                break

    def visit_For(self, node):
        self.newline(node)
        self.write("for ")
        self.visit(node.target)
        self.write(" in ")
        self.visit(node.iter)
        self.body(node.body)

    def visit_While(self, node):
        self.newline(node)
        self.write("while ")
        self.visit(node.test)
        self.body(node.body)

    def visit_With(self, node):
        for withitem in node.items:
            self.newline(node.items)
            self.write("try ")
            self.visit(withitem)
            if withitem != node.items[0]:
                self.write(" {")

        self.body(node.body)
        for i in range(len(node.items) - 1):
            self.newline()
            self.write("}")

    def visit_Pass(self, node):
        pass

    def visit_Delete(self, node):
        self.newline(node)
        self.write("unset ")
        for expr in node.targets:
            if expr != node.targets[0]:
                self.write(", ")
            self.visit(expr)

    def visit_Tuple(self, node):
        if isinstance(node.ctx, Load):
            self.write("tuple(")
        for idx, elt in enumerate(node.elts):
            if idx:
                self.write(", ")
            self.visit(elt)

        if isinstance(node.ctx, Load):
            self.write(")")

    def visit_Try(self, node):
        self.newline(node)
        self.write("try")
        self.body(node.body)
        for handler in node.handlers:
            self.visit(handler)

        if node.finalbody:
            self.newline(node)
            self.write("finally")
            self.body(node.finalbody)

    def visit_Global(self, node):
        self.newline(node)
        self.write("extern " + ", ".join(node.names))

    def visit_Nonlocal(self, node):
        self.newline(node)
        self.write("nonlocal " + ", ".join(node.names))

    def visit_Return(self, node):
        self.newline(node)
        self.write("return")
        if node.value is not None:
            self.write(" ")
            self.visit(node.value)

    def visit_Break(self, node):
        self.newline(node)
        self.write("break")

    def visit_Continue(self, node):
        self.newline(node)
        self.write("continue")

    def visit_Raise(self, node):
        self.newline(node)
        self.write("throw")
        if node.exc is not None:
            self.write(" ")
            self.visit(node.exc)

    def visit_Attribute(self, node):
        if isinstance(node.value, Name) and node.value.id == "self":
            self.write("$%s" % node.attr)
        else:
            if (
                    isinstance(node.value, Call)
                    and isinstance(node.value.func, Name)
                    and node.value.func.id == "super"
            ):
                self.write("super")
                if node.attr != "__init__":
                    self.write("." + node.attr)
            else:
                self.visit(node.value)
                self.write("." + node.attr)

    def visit_Call(self, node):
        want_comma = []

        def write_comma():
            if want_comma:
                self.write(", ")
            else:
                want_comma.append(True)

        self.visit(node.func)
        self.write("(")
        for arg in node.args:
            write_comma()
            self.visit(arg)

        for keyword in node.keywords:
            write_comma()
            self.write(keyword.arg + "=")
            self.visit(keyword.value)

        self.write(")")

    def visit_Name(self, node):
        if node.id == "print":
            self.write("println")
        else:
            if node.id == "chr":
                self.write("char")
            else:
                self.write(node.id)

    def visit_Str(self, node):
        self.write(repr(node.s))

    def visit_Num(self, node):
        self.write(repr(node.n))

    def sequence_visit(left, right):
        def visit(self, node):
            self.write(left)
            for idx, item in enumerate(node.elts):
                if idx:
                    self.write(", ")
                self.visit(item)

            self.write(right)

        return visit

    visit_List = sequence_visit("[", "]")
    visit_Set = sequence_visit("{", "}")
    del sequence_visit

    def visit_Dict(self, node):
        self.write("{")
        empty = True
        for idx, (key, value) in enumerate(zip(node.keys, node.values)):
            empty = False
            if idx:
                self.write(", ")
            self.visit(key)
            self.write(": ")
            self.visit(value)

        if empty:
            self.write(":")
        self.write("}")

    def visit_BinOp(self, node):
        self.visit(node.left)
        self.write(" %s " % BINOP_SYMBOLS[type(node.op)])
        self.visit(node.right)

    def visit_BoolOp(self, node):
        self.write("(")
        for idx, value in enumerate(node.values):
            if idx:
                self.write(" %s " % BOOLOP_SYMBOLS[type(node.op)])
            self.visit(value)

        self.write(")")

    def visit_Compare(self, node):
        self.write("(")
        left = node.left
        emitted = False
        for op, right in zip(node.ops, node.comparators):
            if emitted:
                self.write(" and ")
            else:
                op = CMPOP_SYMBOLS[type(op)]
                if "in" in op:
                    if "not" in op:
                        self.write("!")
                    self.visit(right)
                    self.write(".__contains__(")
                    self.visit(left)
                    self.write(")")
                else:
                    self.visit(left)
                    self.write(" %s " % op)
                    self.visit(right)
            left = right
            emitted = True

        self.write(")")

    def visit_UnaryOp(self, node):
        self.write("(")
        op = UNARYOP_SYMBOLS[type(node.op)]
        self.write(op)
        self.visit(node.operand)
        self.write(")")

    def visit_Subscript(self, node):
        self.visit(node.value)
        self.write("[")
        self.visit(node.slice)
        self.write("]")

    def visit_Slice(self, node):
        if node.lower is not None:
            self.visit(node.lower)
        else:
            self.write(":")
            if node.upper is not None:
                self.visit(node.upper)
            if node.step is not None:
                self.write(":")
                isinstance(node.step, Name) and node.step.id == "None" or self.visit(
                    node.step
                )

    def visit_ExtSlice(self, node):
        for idx, item in node.dims:
            if idx:
                self.write(", ")
            self.visit(item)

    def visit_Yield(self, node):
        self.write("yield ")
        self.visit(node.value)

    def visit_Lambda(self, node):
        self.write("(")
        self.visit(node.args)
        self.write(") -> ")
        self.visit(node.body)

    def visit_ComprehensionExp(self, node, init="", append=""):
        self.write("func() { __ = %s; __.%s(" % (init, append))
        self.visit(node.elt)
        self.write(") ")
        for g in node.generators:
            self.visit(g)

        self.write("; return __ }()")

    def visit_ListComp(self, node):
        self.visit_ComprehensionExp(node, "[]", "append")

    def visit_GeneratorExp(self, node):
        self.write("tuple(")
        self.visit_ComprehensionExp(node, "[]", "append")
        self.write(")")

    def visit_SetComp(self, node):
        self.visit_ComprehensionExp(node, "set()", "add")

    def visit_DictComp(self, node):
        self.write("func() { __ = {;}; __[")
        self.visit(node.key)
        self.write("] = ")
        self.visit(node.value)
        for g in node.generators:
            self.visit(g)

        self.write("; return __ }()")

    def visit_IfExp(self, node):
        self.visit(node.test)
        self.write(" ? ")
        self.visit(node.body)
        self.write(" : ")
        self.visit(node.orelse)

    def visit_Starred(self, node):
        self.write("...")

    def visit_Expr(self, node):
        self.newline()
        if isinstance(node.value, Str):
            self.write("/* %s */" % node.value.s)
        else:
            self.visit(node.value)

    def visit_AnnAssign(self, node):
        self.newline(node)
        self.visit(node.target)
        self.write(" : ")
        self.visit(node.annotation)
        self.write(" = ")
        self.visit(node.value)

    def visit_NameConstant(self, node):
        if node.value == None:
            self.write("nil")
        else:
            if node.value:
                self.write("true")
            else:
                self.write("false")

    def visit_JoinedStr(self, node):
        for id, value in enumerate(node.values):
            if id != 0:
                self.write(" + ")
            self.visit(value)

    def visit_FormattedValue(self, node):
        self.write("str(")
        self.visit(node.value)
        self.write(")")

    def visit_alias(self, node):
        if node.asname is not None:
            self.newline()
            self.write("%s = %s" % (node.asname, node.name))

    def visit_comprehension(self, node):
        if node.ifs:
            for if_ in node.ifs:
                self.write(" if ")
                self.visit(if_)

        self.write(" for ")
        self.visit(node.target)
        self.write(" in ")
        self.visit(node.iter)

    def visit_ExceptHandler(self, node):
        self.newline(node)
        self.write("catch ")
        if node.name is not None:
            self.visit(node.name)
        else:
            self.write("__")
        if node.type is not None:
            self.write(" : ")
            self.visit(node.type)
        self.body(node.body)

    def visit_arguments(self, node):
        self.signature(node)

    def visit_arg(self, node):
        self.write(node.arg)

    def visit_withitem(self, node):
        if node.optional_vars is not None:
            self.visit(node.optional_vars)
            self.write(" = ")
        self.visit(node.context_expr)


def dump(node):
    return to_source(node)

# okay decompiling ./pyc/ulang.CodeGen.ulgen.pyc
